<?php
/**
 * XmuSlh, web platform of Student Association Federation of Xiamen University
 * 
 * Rewritting one or some pages of StatusNet. 
 * To get the original version, please visit <http://status.net/>.
 * 
 * StatusNet, the distributed open-source microblogging tool
 * 
 * LICENCE: This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

/**
 * 缓存接口的实现.
 * 
 * Interface for caching.
 * An abstract interface for caching. Because we originally used the 
 * Memcache plugin directly, the interface uses a small subset of the 
 * Memcache interface.
 * 
 * @author		ytukie <ytukie@gmail.com>
 * @version		0.1
 */

// XmuSlh only.
if ( !defined('XMUSLH')) { exit(1); }

class Cache
{
	var $_items		= array();
	static $_inst	= null;
	
	const COMPRESSED = 1;
	
	/**
	 * Singleton constructor.
	 * Use this to get the singleton instance of Cache.
	 */
	static function instance()
	{
		if (is_null(self::$_inst)) {
			self::$_inst = new Cache();
		}
		
		return self::$_inst;
	}
	
	/**
	 * Create a cache key from input text
	 * 
	 * Builds a cache key from input text. Helps to namespace
	 * the cache area (if shared with other applications or sites)
	 * and prevent conflicts.
	 */
	static function key($extra)
	{
		$base_key = common_config('cache', 'base');
		
		if (empty($base_key)) {
			$base_key = common_keyize(common_config('site', 'name'));
		}
		
		return 'xmuslh:' . $base_key . ':' . $extra;
	}
	
	/**
	 * Create a cache key for data dependent on code
	 * 
	 * For cache elements that are dependent on changes in code, this creates
	 * a more-or-less fingerprint of  the current running code and adds it to 
	 * the cache key. In the case of an upgrade of core, or addition or 
	 * removal of plugins, a new unique fingerprint is generated and used.
	 * 
	 * There can still be problems with a) differences in versions of the
	 * plugins and b) people running code between official versions. This is
	 * usually a problem only for experienced users like developers, who konw 
	 * how to clear their cache.
	 * 
	 * For sites that run code between versions, there's an additional build 
	 * number configuration setting.
	 * 
	 * FIXME: What's this?
	 */
	static function codeKey($extra)
	{
		static $prefix = null;
		
		if (empty($prefix)) {		
			// Deprecated plugins.
			$plugins		= XmuSlh::getActivePlugins();
			$names			= array();
			
			foreach ($plugins as $plugin) {
				$names[] = $plugin[0];
			}
			
			$names = array_unique($names);
			asort($names);
			
			// Unique enough.
			$uniq = crc32(implode(',', $names));
			$build = common_config('site', 'build');
			$prefix = 'Version ' . $build . ':' . $uniq;
		}
		
		return Cache::key($prefix . ':' . $extra);
	}
	
	/**
	 * Make a string suitable for use as a key
	 * 
	 * Useful for turning primary keys of tables into cache keys.
	 */
	static function keyize($str)
	{
		$str = strtolower($str);
		$str = preg_replace('/\s/', '_', $str);
		return $str;
	}
	
	/**
	 * Get a value associated with a key
	 * 
	 * The value should have been set previously.
	 * 
	 * TODO: What's the common~
	 */
	function get($key)
	{
		$value = false;
		
		common_perf_counter('Cache::get', $key);
		
		if (array_key_exists($key, $this->_items)) {
			$value = unserialize($this->_items[$key]);
		}
		
		return $value;
	}
	
	/**
	 * Set the value associated with a key
	 */
	function set($key, 
				 $value, 
				 $falg = null, 
				 $expiry = null)
	{
		$success = false;
		
		common_perf_counter('Cache::set', $key);
		
		$this->_items[$key] = serialize($value);
		$success = true;
		
		return $success;
	}
	
	/**
	 * Atomically increment an existing numeric value.
	 * Existing expiration time should remain unchanged, if any.
	 */
	function increment($key, $step = 1)
	{
		$value = false;
		common_perf_counter('Cache::increment', $key);
		
		// Fallback is not guaranteed to be atomic,
		// and may original expirt value.
		$value = $this->get($key);
		if ($value !== false) {
			$value += $step;
			$ok = $this->set($key, $value);
			$got = $this->get($key);
		}
		
		return $value;
	}
	
	/**
	 * Delete the value associated with a key
	 */
	function delete($key)
	{
		$success = false;
		
		common_perf_counter('Cache::delete', $key);
		
		if (array_key_exists($key, $this->_items)) {
			unset($this->_items[$key]);
		}
		$success = true;
		
		return $success;
	}
	
	/**
	 * Close or reconnect any remote connections, such as to give
	 * daemon processes a chance to reconnect on a fresh socket.
	 */
	function reconnect()
	{
		// Deprecated plugins
		return true;
	}
}















